package game.scene.map.event;
import dgo.tilemap.MapCharacter;
import dgo.interval.*;
import dgo.tilemap.CharacterChipset;
import dgo.MyLib;
import game.scene.SceneStack;
import java.util.Vector;
import org.json.me.JSONArray;
import org.json.me.JSONObject;
import game.scene.map.Model;


public class Event {
  public Vector eventPages, route, saveRoute;
  public EventPage currentEventPage;
  public int routeIndex, saveRouteIndex, moveCount;
  public int vx, vy, id;
  public EventParameters parameters;
  public boolean auto, pauseMovement, pauseCommand;
  public boolean saveRouteSkip, savePauseMovement, saveRouteRepeat;
  public MapCharacter character;
  public Model model;
  public boolean[] selfSwitches;
  public IntervalRunner moveRunner;
  
  public Event(int id, Model model, MapCharacter character){
    vx = 0;
    vy = 0;
    this.id = id;
    this.model = model;
    this.character = character;

    selfSwitches = new boolean[5];
    moveCount = 0;
    pauseCommand = false;
    eventPages = new Vector();
     route = new Vector();
    saveRoute = new Vector();
    moveRunner = new IntervalRunner();
    auto = false;
    
    reset();
  }
  
  public Event getEvent(int no){
    if(no == -1){
      return this;
    }else{
      return model.getEventByID(no);
    }
  }

  public MapCharacter getCharacter(int no){
    return getEvent(no).character;
  }
  
  public MapCharacter player(){
    return model.getCharacter(0);
  }
  
  public Event playerEvent(){
    return model.getEvent(0);
  }
  
  public void push(EventPage ep){
    eventPages.addElement(ep);
  }

  public void reset(){
    parameters = new EventParameters();
    
    pauseMovement = false;
    route = new Vector();
    routeIndex = 0;
    
    saveRoute = new Vector();
    saveRouteIndex = -1; 
    savePauseMovement = false;
    saveRouteRepeat = false;
    saveRouteSkip = false;
  }
  
  public void setInitRoute(Vector route){
    this.route = MyLib.clone(route);
    pauseMovement = false;
    saveRoute = MyLib.clone(route);
    saveRouteIndex = -1; 
    savePauseMovement = pauseMovement;
    saveRouteRepeat = parameters.routeRepeat;
    saveRouteSkip = parameters.routeSkip;
  }
  
  public void saveRoute(){
    if(saveRouteIndex == -1)saveRouteIndex = routeIndex;
    savePauseMovement = pauseMovement;
  }

  public void resetRoute(){
    route = MyLib.clone(saveRoute);
    if(saveRouteIndex >= 0) routeIndex = saveRouteIndex;
    saveRouteIndex = -1;
    pauseMovement = savePauseMovement;
    parameters.routeRepeat =saveRouteRepeat;
    parameters.routeSkip = saveRouteSkip;
  }

  public JSONArray movement(){

    if(routeIndex < 0){
      return null;
    }else{
      if(routeIndex >= route.size()) return null;
      return (JSONArray)route.elementAt(routeIndex);
    }
  }

  public void nextMovement(){
    moveCount = 0;
    vx = 0;
    vy = 0;
    routeIndex += 1;
    if(parameters.routeRepeat){
      if(routeIndex >= route.size()){
        routeIndex = 0;
      }
    }
  }


  public void move(int tx,int ty){
    moveCount -= 1;
    if(moveCount < 0){
      character.dirX = (byte)tx;
      character.dirY = (byte)ty;
      moveCount = 0;
      nextMovement();
    }else{
      if((vx == 0)&&(vy == 0)){
        vx = tx;
        vy = ty; 
      }
      if(character.setMovement(vx, vy)){
        if(moveCount == 0){nextMovement();}
      }else{
        if(parameters.routeSkip){nextMovement();}
      }
    }
/*
if @character.set_movement(@vx, @vy, :after_interval => Wait.new(@parameters.wait))
*/
  }
  
  /* ---------------------------------
   Switch On / Off / Reverse
   ---------------------------------*/ 
  public void setSwitch(int no, String type){
    if(type.equals("reverse")){
      model.globalModel.switches[no] = !model.globalModel.switches[no];
    }else{
      model.globalModel.switches[no] = (type.equals("on"));
    }
  }

  public void executeMovement(){
    if(!moveRunner.isDone()){moveRunner.update();}

    if(pauseMovement || character.isRunning() || movement() == null) return;
    try{
      JSONArray movement = movement();
      String type = movement.getString(0);

      int tx = 0, ty = 0;
      
      //System.out.println(type);
      if(type.equals("move")){
        int count = Integer.parseInt(movement.getString(2));
        if(count > 0 && moveCount == 0){
          moveCount = count;
        }
        String subType = movement.getString(1);
        if(subType.equals("up")){ move(0, -1);}
        if(subType.equals("down")){ move(0, 1);}
        if(subType.equals("right")){ move(1, 0);}
        if(subType.equals("left")){ move(-1, 0);}
        if(subType.equals("upper_left")){ move(-1, -1);}
        if(subType.equals("upper_right")){ move(1, -1);}
        if(subType.equals("bottom_left")){ move(-1, 1);}
        if(subType.equals("bottom_right")){ move(1, 1);}
        if(subType.equals("to")){ 
          character.faceTo(this.player());
          tx = character.dirX;
          ty = character.dirY;
          this.move(tx, ty);
        }
        if(subType.equals("against")){
          character.faceTo(this.player());
          tx = -character.dirX;
          ty = -character.dirY;
          this.move(tx, ty);
        }
        if(subType.equals("random")){
          int t = MyLib.rand(4);
          tx = MyLib.extractDirX(t);
          ty = MyLib.extractDirY(t);
        }
        if(subType.equals("forward")){
          tx = character.dirX;
          ty = character.dirY;
          this.move(tx, ty);
        }
        if(subType.equals("backward")){
          tx = -character.dirX;
          ty = -character.dirY;
          this.move(tx, ty);
        }
        return;
      }

      if(type.equals("wait")){
        character.setWait(Integer.parseInt(movement.getString(1)));
        this.nextMovement();
        return;
      }
      
      if(type.equals("play_se")){
        if(movement.getString(1).equals("")){
          // stop all se
        }else{
          // play se
          MyLib.playSE(getClass(), movement.getString(1));
        }
        this.nextMovement();
        return;
      }
      
      if(type.equals("switch")){
        String s = movement.getString(1);
        if(s.equals("single")){
          setSwitch(movement.getInt(2), movement.getString(3));
        }else if(s.equals("area")){
          for(int i = movement.getInt(2); i < movement.getInt(3);i++){
            setSwitch(i, movement.getString(4));
          }
        }
        this.nextMovement();
        return;
      }
      if(type.equals("anime")){
        character.moveAnime = movement.getBoolean(1);
        character.stayAnime = movement.getBoolean(2);
        this.nextMovement();
        return;
      }
      if(type.equals("pass")){
        character.passTile = movement.getBoolean(1);
        character.passCharacters = movement.getBoolean(2);
        character.passEvent = movement.getBoolean(3);
        this.nextMovement();
        return;
      }
      if(type.equals("visible")){
        character.visible = true;
        this.nextMovement();
        return;
      }
      if(type.equals("invisible")){
        character.visible = false;
        this.nextMovement();
        return;
      }
      if(type.equals("dir_fix")){
        character.dirFix = true;
        this.nextMovement();
        return;
      }
      if(type.equals("dir_free")){
        character.dirFix = false;
        this.nextMovement();
        return;
      }
      if(type.equals("set_speed")){
        character.setSpeedType(Byte.parseByte(movement.getString(1)));
        this.nextMovement();
        return;
      }
      if(type.equals("speed_up")){
        character.setSpeedType((byte)(character.getSpeedType() + 1));
        this.nextMovement();
        return;
      }
      if(type.equals("speed_down")){
        character.setSpeedType((byte)(character.getSpeedType() - 1));
        this.nextMovement();
        return;
      }
      if(type.equals("set_graphic")){
        setCharacterChip(model, movement.getString(1), Integer.parseInt(movement.getString(2)), Integer.parseInt(movement.getString(3)));
        this.nextMovement();
        return;
      }
    }catch(Exception e){
      System.out.println("ExecuteMovement Failed: " + e);
    }
  }

  public void setCharacterChip(Model model, String chipsetID, int sx, int sy){
    if(chipsetID != null && chipsetID.equals("null")){chipsetID = null;}
    CharacterChipset chipset = model.getCharacterChipset(chipsetID);
    if(chipset != null){
      character.setDir(sy % chipset.dirNumber);
      character.frameNo = (byte)(sx % chipset.animationFrameNumber);
      character.setChip(chipset, (sx / chipset.animationFrameNumber), (sy / chipset.dirNumber));
    }else{
      character.setChip(null, 0, 0);
    }
  }

  public boolean isConditionValid(JSONArray condition){
    boolean b = true;
    try{
      String arg0 = condition.getString(0);

      // Variable
      if(arg0.equals("variable")){
        int arg1 = condition.getint(1);
        String arg2 = condition.getString(2);
        String arg3 = condition.getString(3);
        int arg4 = condition.getint(4);
        int v1 = model.globalModel.variables[arg1];
        int v2 = 0;
        if(arg3.equals("var")){
          v2 = model.globalModel.variables[arg4];
        }else if(arg3.equals("val")){
          v2 = arg4;
        }
        
        if(arg2.equals(">")){
          if(v1 <= v2){b = false;}
        }else if(arg2.equals(">=")){
          if(v1 < v2){b = false;}
        }else if(arg2.equals("==")){
          if(v1 != v2){b = false;}
        }else if(arg2.equals("<=")){
          if(v1 > v2){b = false;}
        }else if(arg2.equals("<")){
          if(v1 >= v2){b = false;}
        }
        return b;
      }

      // Switch
      if(arg0.equals("switch")){
        int arg1 = Integer.parseInt(condition.getString(1));
        String arg2 = condition.getString(2);
        if(arg2.equals("on")){
          if(!model.globalModel.switches[arg1]){b = false;}
        }else{
          if(model.globalModel.switches[arg1]){b = false;}
        }
        return b;
      }

      // Self Switch
      if(arg0.equals("self_switch")){
        int arg1 = Integer.parseInt(condition.getString(1));
        String arg2 = condition.getString(2);
        if(arg2.equals("on")){
          if(!selfSwitches[arg1]){b = false;}
        }else{
          if(selfSwitches[arg1]){b = false;}
        }
        return b;
      }

      // Dir
      if(arg0.equals("dir")){
        int arg1 = Integer.parseInt(condition.getString(1));
        int arg2 = Integer.parseInt(condition.getString(2));
        MapCharacter targetCharacter = getCharacter(arg1);
        if(targetCharacter.getCurrentDir() != arg2){b = false;}
        return b;
      }
    
    }catch(Exception e){
      System.out.println("Condition Valid Failed: " + e);
    }
    return b;
  }
  
  public void update(SceneStack stack){
    if(eventPages.isEmpty()) return;
    
    // if current page has already been activated, just call it.
    if(currentEventPage != null && currentEventPage.active){
      currentEventPage.executeCommand(stack);
      return;
    }

    // Go through event pages
    for(int i = eventPages.size() - 1; i >= 0; i--){

      EventPage eventPage = (EventPage)(eventPages.elementAt(i));

      if(!eventPage.isValid()){continue;}
      
      // When the page is changed
      if(currentEventPage != eventPage){ eventPage.onValidate(); }
      
      // First time call the command of the page
      if(eventPage.isTriggered()){
        eventPage.onCommandStart();
        eventPage.executeCommand(stack);
      }
      
      currentEventPage = eventPage;
      return;
    }
    // If no condition met
    if(currentEventPage != null){
      currentEventPage = null;
      character.setChip(null, 0, 0);
      character.passTile = true;
      character.passCharacters = true;
      character.passEvent = true;
      this.reset();
    }
  }
  
  public void updateMovement(){
    executeMovement();
  }
}
